﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathNet.Numerics.LinearAlgebra.Single;
using ManagedCuda.CudaBlas;

namespace cudaMatrix
{
    public enum ComputationHost {CPU, CUDA}

    public class MatrixFactory
    {
        private static ComputationHost computationHost = ComputationHost.CUDA;

        private static Dictionary<String, Func<object, object, DenseMatrix>> functions = null;
        private static Dictionary<String, Func<object, float>> functions_one_param = null;

        //ustawia srodowisko obliczen
        public static ComputationHost COMPUTER
        {
            get
            {
                return computationHost;
            }
            set
            {
                computationHost = value;
            }
        }

        //tworzy nowa macierz
        public static DenseMatrix createMatrix(int rows, int columns, float[] values)
        {
            switch (computationHost)
            {
                case ComputationHost.CPU:
                    return new DenseMatrix(rows, columns, values);
                case ComputationHost.CUDA:
                    return new CudaMatrix(rows, columns, values);
            }
            return new DenseMatrix(rows, columns, values);
        }

        public static DenseMatrix createMatrix(int rows, int columns, float value)
        {
            switch (computationHost)
            {
                case ComputationHost.CPU:
                    return new DenseMatrix(rows, columns, value);
                case ComputationHost.CUDA:
                    return new CudaMatrix(rows, columns, value);
            }
            return new DenseMatrix(rows, columns, value);
        }

        public static Dictionary<String, Func<object, object, DenseMatrix>> getAllowedOperations()
        {
            if (functions == null)
                generateFunctions();
            return functions;
        }

        public static Dictionary<String, Func<object, float>> getAllowedOperationsOneParam()
        {
            if (functions_one_param == null)
                generateFunctions();
            return functions_one_param;
        }

        public static DenseMatrix Zeros(object rows, object columns)
        {
            return createMatrix((int)rows, (int)columns, 0);
        }

        public static DenseMatrix Ones(object rows, object columns)
        {
            return createMatrix((int)rows, (int)columns, 1);
        }

        public static DenseMatrix ValueWiseMultiply(object m1, object m2)
        {
            var A = m1 as DenseMatrix;
            var B = m2 as DenseMatrix;
            if (A.RowCount == B.RowCount && A.ColumnCount == B.ColumnCount)
            {
                var t1 = A.Data;
                var t2 = B.Data;
                var t3 = new float[t1.Length];
                for (int i = 0; i < t1.Length; i++)
                {
                    t3[i] = t1[i] * t2[i];
                }

                return createMatrix(A.RowCount, A.ColumnCount, t3);
            }
            else
            {
                return null;
            }

        }

        public static DenseMatrix ValueWiseDivide(object m1, object m2)
        {
            var A = m1 as DenseMatrix;
            var B = m2 as DenseMatrix;
            if (A.RowCount == B.RowCount && A.ColumnCount == B.ColumnCount)
            {
                var t1 = A.Data;
                var t2 = B.Data;
                var t3 = new float[t1.Length];
                for (int i = 0; i < t1.Length; i++)
                {
                    t3[i] = t1[i] / t2[i];
                }

                return createMatrix(A.RowCount, A.ColumnCount, t3);
            }
            else
            {
                return null;
            }

        }

        private static void generateFunctions()
        {
            functions = new Dictionary<string,Func<object,object,DenseMatrix>>();
            functions.Add("zeros", Zeros);
            functions.Add("ones", Ones);
            functions.Add("*.", ValueWiseMultiply);
            functions.Add("/.", ValueWiseDivide);

            functions_one_param = new Dictionary<string, Func<object, float>>();
            functions_one_param.Add("det", Det);
            functions_one_param.Add("min", Min);
            functions_one_param.Add("max", Max);
        }

        public static float Det(Object m)
        {
            return (m as DenseMatrix).Determinant();
        }

        public static float Max(Object m)
        {
            return (m as DenseMatrix).Data.Max();
        }

        public static float Min(Object m)
        {
            return (m as DenseMatrix).Data.Min();
        }

    }
}
